/* Part of the KnowARC Janitor Use-case processor for taverna
 *  written 2007-2010 by Hajo Nils Krabbenhoeft and Steffen Moeller
 *  University of Luebeck, Institute for Neuro- and Bioinformatics
 *  University of Luebeck, Institute for Dermatolgy
 *
 *  This package is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This package is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this package; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

package de.uni_luebeck.inb.knowarc.usecases.invocation;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import org.globus.ftp.exception.NotImplementedException;

import de.uni_luebeck.inb.knowarc.grid.ProgressDisplay;
import de.uni_luebeck.inb.knowarc.usecases.ScriptOutput;
import de.uni_luebeck.inb.knowarc.usecases.UseCaseDescription;

/**
 * The job is executed locally, i.e. not via the grid.
 * @author Hajo Krabbenhoeft
 */
public class LocalUseCaseInvokation extends UseCaseInvokation {

	private final File tempFile;
	private final File tempDir;
	private final ProgressDisplay progressDisplay;

	public LocalUseCaseInvokation(ProgressDisplay progressDisplay,UseCaseDescription desc) throws IOException {
		this.progressDisplay = progressDisplay;
		usecase = desc;

		tempFile = File.createTempFile("usecase", "di");
		tempDir = new File(tempFile.getAbsolutePath()+"r");
		tempDir.mkdir();
	}

	void recDel(File c) {
		File[] files = c.listFiles();
		if (files != null) {
			for (File cc : files)
				recDel(cc);
		}
		c.delete();
	}

	@Override
	public void putFile(String name, byte[] contents) {
		try {
			FileOutputStream out = new FileOutputStream(tempDir.getAbsolutePath() + "/" + name, false);
			out.write(contents);
			out.close();
		} catch (Exception e) {
			progressDisplay.log(0, "putFile failed for "+name);
			progressDisplay.logTrace(1, e);
		}
	}

	@Override
	public void putReference(String name, String source) throws NotImplementedException {
		throw new NotImplementedException();
	}

	@Override
	public void Cleanup() {
		recDel(tempDir);
		tempFile.delete();
	}

	/**
	 * Transforms an input stream towards an entity that Taverna can work with.
	 * @param read
	 * @param binary
	 * @return
	 * @throws IOException
	 */
	private Object file2data(InputStream read, boolean binary) throws IOException {
		byte[] data = new byte[0];
		byte[] buf = new byte[1024];
		int r;
		while(-1 != (r = read.read(buf))) {
			byte[] d2 = new byte[data.length + r];
			System.arraycopy(data, 0, d2, 0, data.length);
			System.arraycopy(buf, 0, d2, data.length, r);
			data = d2;
		}
		if (binary)
			return data;
		else
			return Charset.forName("US-ASCII").decode(ByteBuffer.wrap(data)).toString();
	}

	private Process running;

	@Override
	protected void submit_generate_job_inner() throws Exception {
		tags.put("uniqueID", "" + getSubmissionID());
		String command = usecase.getCommand();
		for (String cur : tags.keySet()) {
			command = command.replaceAll("\\Q%%" + cur + "%%\\E", tags.get(cur));
		}
		progressDisplay.log(2, "Directory: " + tempDir.getAbsolutePath());
		progressDisplay.log(2, "Command: " + command);
		ProcessBuilder builder = new ProcessBuilder(command.split(" "));
		builder.directory(tempDir);
		builder.environment();
		running = builder.start();
	}

	private void copy_stream(InputStream read, OutputStream write) throws IOException {
		int a = read.available();
		if (a > 0) {
			byte[] buf = new byte[a];
			read.read(buf);
			write.write(buf);
		}
	}

	@Override
	public HashMap<String, Object> submit_wait_fetch_results() throws Exception {
		ByteArrayOutputStream stdout_buf = new ByteArrayOutputStream();
		ByteArrayOutputStream stderr_buf = new ByteArrayOutputStream();
		while (true) {
			copy_stream(running.getInputStream(), stdout_buf);
			copy_stream(running.getErrorStream(), stderr_buf);
			try {
				int errorCode = running.exitValue();
				if (errorCode != 0)
					throw new Exception("Exit code: " + errorCode);
				else
					break;
			} catch (IllegalThreadStateException e) {
				try {
					Thread.sleep(100);
				} catch (Exception e2) {
				}
			}
		}

		HashMap<String, Object> results = new HashMap<String, Object>();
		results.put("STDOUT", Charset.forName("US-ASCII").decode(ByteBuffer.wrap(stdout_buf.toByteArray())).toString());
		results.put("STDERR", Charset.forName("US-ASCII").decode(ByteBuffer.wrap(stderr_buf.toByteArray())).toString());

		for (Map.Entry<String, ScriptOutput> cur : usecase.outputs.entrySet()) {
			results.put(cur.getKey(), file2data(new FileInputStream(tempDir.getAbsoluteFile() + "/" + cur.getValue().path), cur.getValue().binary));
		}

		return results;
	}

}
